/**
 * Copyright (C) 2013 DaiKit.com - daikit4gxt module (admin@daikit.com)
 *
 *         Project home : http://code.daikit.com/daikit4gxt
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.daikit.daikit4gxt.client.editor;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.google.gwt.editor.client.EditorDelegate;
import com.google.gwt.editor.client.ValueAwareEditor;
import com.sencha.gxt.data.shared.TreeStore;
import com.sencha.gxt.data.shared.TreeStore.TreeNode;


/**
 * An Editor for {@link TreeStore}
 * 
 * @author tcaselli
 * @version $Revision$ Last modifier: $Author$ Last commit: $Date$
 * @param <T>
 */
public class TreeStoreEditor<T extends TreeStore.TreeNode<T>> implements ValueAwareEditor<List<T>>
{
	private final TreeStore<T> store;
	private List<T> model;

	private final Set<Class<? extends T>> folderClasses = new HashSet<Class<? extends T>>();
	private final Set<Class<? extends T>> leafClasses = new HashSet<Class<? extends T>>();

	/**
	 * Creates an editor use with a {@link TreeStore}.<br>
	 * <ul>
	 * <li>will be considered as folders items with children</li>
	 * <li>will be considered as leaves items without child</li>
	 * </ul>
	 * 
	 * 
	 * @param store
	 *           the tree store that uses this editor
	 */
	public TreeStoreEditor(final TreeStore<T> store)
	{
		this(store, null, null);
	}

	/**
	 * Creates an editor use with a {@link TreeStore}.
	 * 
	 * @param store
	 *           the tree store that uses this editor
	 * @param folderClasses
	 *           a {@link Set} of classes to be considered as folders, if null or empty, will be considered as folders
	 *           items with children
	 * @param leafClasses
	 *           a {@link Set} of classes to be considered as leaves, if null or empty, will be considered as leaves
	 *           items without child
	 */
	public TreeStoreEditor(final TreeStore<T> store, final Set<Class<? extends T>> folderClasses,
			final Set<Class<? extends T>> leafClasses)
	{
		if (folderClasses != null)
		{
			this.folderClasses.addAll(folderClasses);
		}
		if (leafClasses != null)
		{
			this.leafClasses.addAll(leafClasses);
		}
		this.store = store;
	}

	@Override
	public void flush()
	{
		// make any modifications to the models themselves
		store.commitChanges();

		// flush out the contents of the list, so structural changes are made as
		// well
		if (model != null)
		{
			model.clear();
			model.addAll(store.getAll());
		} // TODO add an else here to create a list?
	}

	/**
	 * Returns the list store that uses this editor.
	 * 
	 * @return the list store that uses this editor.
	 */
	public TreeStore<T> getStore()
	{
		return store;
	}

	@Override
	public void onPropertyChange(final String... paths)
	{
		// as ListEditor, noop
	}

	@Override
	public void setDelegate(final EditorDelegate<List<T>> delegate)
	{
		// ignore for now, this could be used to pass errors into the view
	}

	@Override
	public void setValue(final List<T> value)
	{
		// replace the data in the store (maybe use a loader for this?)
		store.clear();
		if (value != null && value.size() > 0)
		{
			for (final T rootItem : value)
			{
				store.add(rootItem);
				if ((folderClasses.isEmpty() && !leafClasses.contains(rootItem.getClass()))
						|| folderClasses.contains(rootItem.getClass()))
				{
					processFolder(rootItem);
				}
			}
		}
		// store a copy of the original list, so changes can be pushed to that
		this.model = value;
	}

	@SuppressWarnings("unchecked")
	protected void processFolder(final T folder)
	{
		for (final TreeNode<T> child : folder.getChildren())
		{
			store.add(folder, (T) child);
			if ((folderClasses.isEmpty() && !leafClasses.contains(child.getClass())) || folderClasses.contains(child.getClass()))
			{
				processFolder((T) child);
			}
		}
	}
}
